﻿using Spire.Doc;
using Spire.Doc.Collections;
using Spire.Doc.Documents;
using Spire.Doc.Fields;
using Spire.Doc.Formatting;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace OfficeHelper
{
	public class DocConvert
	{
		public static string DocToHTML(DocumentObject docObj)
		{
			string result = "";
			switch (docObj.DocumentObjectType)
			{
				case DocumentObjectType.Section:
					{
						Section section = docObj as Section;
						result = CreateElement("section", GetAttrs(section), ParseChildren(section.Body.ChildObjects));
						break;
                    }
                case DocumentObjectType.Body:
                    {
                        Body body = docObj as Body;
                        result = CreateElement("div", GetAttrs(body), ParseChildren(body.ChildObjects));
                        break;
                    }
                case DocumentObjectType.Table:
					{
						Table table = docObj as Table;
						result = CreateElement("table", GetAttrs(table), ParseChildren(table.Rows));
						break;
					}
				case DocumentObjectType.TableRow:
					{
						TableRow tableRow = docObj as TableRow;
						result = CreateElement("tr", GetAttrs(tableRow), ParseChildren(tableRow.Cells));
						break;
					}
				case DocumentObjectType.TableCell:
					{
						TableCell tableCell = docObj as TableCell;
						result = CreateElement("td", GetAttrs(tableCell), ParseChildren(tableCell.ChildObjects));
						break;
					}
				case DocumentObjectType.Paragraph:
					{
						Paragraph para = docObj as Paragraph;
						result = CreateElement("div", GetAttrs(para), ParseChildren(para.ChildObjects));
						break;
					}
				case DocumentObjectType.TextRange:
					{
						TextRange textRange = docObj as TextRange;
						result = CreateElement("span", GetAttrs(textRange), KeepSpaces(textRange.Text));
						break;
					}
				case DocumentObjectType.Picture:
					{
						break;
					}
			}
			return result;
		}

        private static string ParseChildren(DocumentObjectCollection children)
		{
			string result = "";
			foreach(DocumentObject docObj in children)
			{
				result += DocToHTML(docObj);
			}
			return result;
		}

        public static void HTMLToDoc(DocumentObject docObj, string html)
        {
            Document doc = new Document();
            doc.AddSection().AddParagraph().AppendHTML(html);

            switch (docObj.DocumentObjectType)
            {
                case DocumentObjectType.Body:
                    {
                        Body body = docObj as Body;
                        CharacterFormat cf = body.Paragraphs[0].BreakCharacterFormat;
                        body.Paragraphs.Clear();
                        foreach(Paragraph p in doc.Sections[0].Paragraphs)
                        {
                            ApplyStyleToParagraph(p, cf);
                            body.Paragraphs.Add((Paragraph)p.Clone());
                        }
                        break;
                    }
                case DocumentObjectType.Paragraph:
                    {
                        Paragraph p = docObj as Paragraph;
                        p.ChildObjects.Clear();
                        foreach(DocumentObject child in doc.Sections[0].Paragraphs[0].ChildObjects)
                        {
                            if(child.DocumentObjectType == DocumentObjectType.TextRange)
                            {
                                p.ChildObjects.Add(child.Clone());
                            }
                        }
                        break;
                    }
                case DocumentObjectType.TextRange:
                    {
                        TextRange textRange = docObj as TextRange;
                        if(html.IndexOf("<span") != 0)
                        {
                            textRange.Text = html;
                        }
                        else
                        {
                            TextRange temp = doc.Sections[0].Paragraphs[0].ChildObjects[0] as TextRange;
                            textRange.Text = temp.Text;
                            ApplyStyle(textRange.CharacterFormat, temp.CharacterFormat);
                        }
                        break;
                    }
            }
        }

        private static string KeepSpaces(string value)
        {
            string result = "";
            for(int i = 0; i < value.Length; i++)
            {
                if(value[i] == ' ')
                {
                    result += "&nbsp;";
                }
                else
                {
                    result += value[i];
                }
            }
            return result;
        }

        private static void ApplyStyleToParagraph(Paragraph para, CharacterFormat cf)
        {
            foreach (DocumentObject child in para.ChildObjects)
            {
                if (child.DocumentObjectType == DocumentObjectType.TextRange)
                {
                    TextRange tr = child as TextRange;
                    ApplyStyle(tr.CharacterFormat, cf);
                }
            }
        }

        private static void ApplyStyle(CharacterFormat cf1, CharacterFormat cf2)
        {
            bool bold = cf1.Bold;
            cf1.Font = cf2.Font;
            if (bold)
            {
                cf1.Bold = bold;
            }

            if (cf1.TextColor.Name == "0")
            {
                cf1.TextColor = cf2.TextColor;
            }
            cf1.UnderlineStyle = UnderlineStyle.None;
        }


        private static Dictionary<string, string> GetAttrs(DocumentObject docObj)
		{
			Dictionary<string, string> attrs = new Dictionary<string, string>();
			Dictionary<string, string> style = new Dictionary<string, string>();
			switch (docObj.DocumentObjectType)
			{
				case DocumentObjectType.Section:
					{
						Section section = docObj as Section;
						break;
                    }
                case DocumentObjectType.Body:
                    {
                        Body body = docObj as Body;
                        break;
                    }
                case DocumentObjectType.Table:
					{
						Table table = docObj as Table;
						break;
					}
				case DocumentObjectType.TableRow:
					{
						TableRow tableRow = docObj as TableRow;
						break;
					}
				case DocumentObjectType.TableCell:
					{
						TableCell tableCell = docObj as TableCell;
						break;
					}
				case DocumentObjectType.Paragraph:
					{
						Paragraph para = docObj as Paragraph;
                        attrs.Add("style", StyleToStr(GetStyle(para.BreakCharacterFormat)));
                        break;
					}
				case DocumentObjectType.TextRange:
					{
						TextRange textRange = docObj as TextRange;
                        attrs.Add("style", StyleToStr(GetStyle(textRange.CharacterFormat)));
                        break;
					}
				case DocumentObjectType.Picture:
					{
						break;
					}
			}
			return attrs;
		}

        public static string CreateElement(string name, Dictionary<string, string> attrs, string content)
		{
			return $"<{name}{AttrsToStr(attrs)}>{content}</{name}>";
		}

        public static string CreateElement(string name, Dictionary<string, string> attrs)
		{
			return $"<{name}{AttrsToStr(attrs)}/>";
		}

        private static string AttrsToStr(Dictionary<string, string> attrs)
		{
			string result = "";
			if(attrs != null)
			{
				foreach (var attr in attrs)
				{
					result += $" {attr.Key}=\"{attr.Value}\"";
				}
			}
			return result;
		}

        private static Dictionary<string, string> GetStyle(CharacterFormat cf)
        {
            Dictionary<string, string> result = new Dictionary<string, string>();
            if (cf.Bold)
            {
                result.Add("font-weight", "bold");
            }
            if (cf.TextColor.Name != "0")
            {
                result.Add("color", cf.TextColor.Name);
            }
            return result;
        }

        public static string StyleToStr(Dictionary<string, string> style)
		{
			string result = "";
			if (style != null)
			{
				foreach (var s in style)
				{
					result += $"{s.Key}:{s.Value};";
				}
			}
			return result;
		}
	}
}
